package com.auntvt.timo.jpax.springbootstarter.resolver;

import com.auntvt.timo.jpax.core.exception.JpaxException;
import com.auntvt.timo.jpax.core.quick.enums.MatcherType;
import com.auntvt.timo.jpax.core.quick.model.Matcher;
import com.auntvt.timo.jpax.core.utils.SpringContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Matcher 请求方法参数解析器
 *
 * @author 小懒虫 <auntvt@163.com>
 * @date 2021/7/31
 */
public class MatcherMethodArgumentResolver implements HandlerMethodArgumentResolver {

    /**
     * 条件规则集合请求参数名称，数据格式： rules=xx:eq,xx:LIKE
     */
    private static final String RULES_NAME = "rules";

    /**
     * 条件规则项请求参数名称，数据格式： xx_rule=eq&xx_rule=LIKE
     */
    private static final String RULE_ITEM_SUFFIX_NAME = "_rule";

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().isAssignableFrom(Matcher.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        // 分离查询字段及规则
        Object params = new LinkedHashMap<String, String[]>();
        @SuppressWarnings("unchecked")
        Map<String, String[]> finalParams = (Map<String, String[]>) params;
        Matcher.Rules rules = new Matcher.Rules();
        webRequest.getParameterMap().forEach((name, values) -> {
            // 判断参数名称是否为 RULES_NAME
            if (StringUtils.equals(name, RULES_NAME)) {
                rules.putAll(parseRules(values[values.length - 1]));
            }
            // 判断参数名称后缀是否为 RULE_ITEM_SUFFIX_NAME
            else if (StringUtils.endsWith(name, RULE_ITEM_SUFFIX_NAME)) {
                rules.put(name.replaceAll(RULE_ITEM_SUFFIX_NAME, ""), toMatcherType(values[values.length - 1]));
            }
            // 否则参数为查询字段
            else {
                finalParams.put(name, values);
            }
        });

        // 获取Matcher参数对象类型，创建对象
        Type type = parameter.getGenericParameterType();
        if (type instanceof ParameterizedType) {
            Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
            if (typeArguments.length > 0 && !((Class<?>) typeArguments[0]).isAssignableFrom(Map.class)) {
                params = ((Class<?>) typeArguments[0]).newInstance();
                PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(params.getClass());
                for (PropertyDescriptor descriptor : descriptors) {
                    String name = descriptor.getName();
                    if (finalParams.containsKey(name)) {
                        String[] values = finalParams.get(name);
                        Method writeMethod = descriptor.getWriteMethod();
                        ConversionService conversion = SpringContextUtil.getBean(ConversionService.class);
                        Object value = conversion.convert(values[values.length - 1], writeMethod.getParameterTypes()[0]);
                        writeMethod.invoke(params, value);
                    }
                }
            }
        }

        return new Matcher<>(params, rules);
    }

    /**
     * 解析rules
     *
     * @param rules 内容
     * @return Map
     */
    private Map<String, MatcherType> parseRules(String rules) {
        Map<String, MatcherType> result = new LinkedHashMap<>();
        String[] items = rules.split(",");
        for (String item : items) {
            String[] split = item.split(":");
            if (split.length != 2) {
                throw new JpaxException("[{0}]参数值不符合规范：参数名:条件类型,xx:xx,...", RULES_NAME);
            }
            result.put(split[0], toMatcherType(split[1]));
        }
        return result;
    }

    /**
     * 将参数规则的值转为MatcherType枚举类
     */
    private MatcherType toMatcherType(String value) {
        try {
            return MatcherType.valueOf(StringUtils.upperCase(value));
        } catch (IllegalArgumentException e) {
            throw new JpaxException("[{0}]规则类型不存在！", value);
        }
    }
}